package com.google.code.coma.space.util;

import com.google.code.coma.space.ComaFIFOCoordinator;
import com.google.code.coma.space.Id;
import com.google.code.coma.space.SpaceEntity;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.mozartspaces.capi3.Capi3AspectPort;
import org.mozartspaces.capi3.Coordinator;
import org.mozartspaces.capi3.SubTransaction;
import org.mozartspaces.capi3.Transaction;
import org.mozartspaces.core.*;
import org.mozartspaces.core.aspects.AbstractContainerAspect;
import org.mozartspaces.core.aspects.AspectResult;
import org.mozartspaces.core.aspects.ContainerAspect;
import org.mozartspaces.core.aspects.ContainerIPoint;
import org.mozartspaces.core.requests.WriteEntriesRequest;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.net.URI;
import java.util.*;

/**
 * @author Dominik Strasser, dominikstr@gmail.com
 */
public class SpaceManager {
	public static ContainerReference getOrCreateNamedContainer(final URI space, final String containerName,
															   final Capi capi) throws MzsCoreException {
		ContainerReference cref;
		try {
			// Get the Container
			cref = capi.lookupContainer(containerName, space, MzsConstants.RequestTimeout.DEFAULT, null);
			// If it is unknown, create it
		} catch (MzsCoreException e) {
			// Create the Container
			ArrayList<Coordinator> obligatoryCoords = new ArrayList<Coordinator>();
			obligatoryCoords.add(new ComaFIFOCoordinator());
			cref = capi.createContainer(containerName, space, MzsConstants.Container.UNBOUNDED, obligatoryCoords, null, null);
			//define the aspect which creates unique ids
			ContainerAspect aspect = new AbstractContainerAspect() {
				private final Log log = LogFactory.getLog(ContainerAspect.class);

				@Override
				public AspectResult preWrite(WriteEntriesRequest request, Transaction tx, SubTransaction stx, Capi3AspectPort capi3, int executionCount) {
					Iterator<Entry> it = request.getEntries().iterator();
					while (it.hasNext()) {
						Entry e = it.next();
						Serializable value = e.getValue();
						SpaceEntity annotation = value.getClass().getAnnotation(SpaceEntity.class);
						if (annotation != null) {
							//okay, it's a spaceentity
							Field[] fields = value.getClass().getDeclaredFields();
							for (Field f : fields) {
								Id id = f.getAnnotation(Id.class);
								if (id != null) {
									boolean access = f.isAccessible();
									f.setAccessible(true);
									//we got an id
									try {
										if (f.get(value) == null) {
											if (id.generator() == Id.GenerationType.AUTO_GENERATED) {
												if (f.getType() == String.class) {
													String generatedId = "" + annotation.value() + UUID.randomUUID();
													f.set(value, generatedId);
												} else {
													if (log.isDebugEnabled()) {
														log.debug("Unable to generate non string id");
													}
												}
											}
										}
									} catch (IllegalAccessException e1) {
										log.error("Error in prosessing space entity", e1);
										return AspectResult.SKIP;
									} finally {
										f.setAccessible(access);
									}
									break;
								}
							}
						}
					}
					return AspectResult.OK;
				}
			};
			Set<ContainerIPoint> ipoints = new HashSet<ContainerIPoint>();
			ipoints.add(ContainerIPoint.PRE_WRITE);
			capi.addContainerAspect(aspect, cref, ipoints, null);
		}
		return cref;
	}
}
